package com.jrx.anytxn.transaction.service.impl;

import com.jrx.anytxn.common.uitl.BeanUtils;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.BigDecimalUtils;
import com.jrx.anytxn.transaction.bean.DebtBean;
import com.jrx.anytxn.transaction.bean.DebtDetailBean;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.entity.AmAccountMainInfo;
import com.jrx.anytxn.transaction.entity.AmInterestAccrualInfo;
import com.jrx.anytxn.transaction.entity.AmLoanAccountInfo;
import com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmAccountMainInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmInterestAccrualInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.service.IDebtCalculationService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 欠款计算实现
 *
 * @author zhenghaopu
 */
@Service
public class DebtCalculationServiceImpl implements IDebtCalculationService {

    private static final Logger logger = LoggerFactory.getLogger(DebtCalculationServiceImpl.class);

    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Resource
    private ExtAmAccountMainInfoMapper extAmAccountMainInfoMapper;
    @Resource
    private ExtAmInterestAccrualInfoMapper extAmInterestAccrualInfoMapper;


    /**
     * 根据交易账户获取欠款
     *
     * @param amAccountMainInfoList 交易账户list
     * @return 欠款信息
     * @author jing.xiaoyu
     */
    @Override
    public DebtBean calculationDebt(List<AmAccountMainInfo> amAccountMainInfoList) {
        DebtBean debtBean = new DebtBean();
        if (CollectionUtils.isEmpty(amAccountMainInfoList)) {
            return debtBean;
        }
        //将交易账户按交易类型分组求欠款之和
        Map<String, BigDecimal> acctMap = amAccountMainInfoList.stream()
                .collect(Collectors.groupingBy(AmAccountMainInfo::getAccountType,
                        CollectorsUtils.summingBigDecimal(AmAccountMainInfo::getCurrBalance)));

        //本金
        BigDecimal principalAmount = Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO);
        //利息
        BigDecimal interestAmount = Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
        //罚息
        BigDecimal penaltyAmount = Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO);
        //罚息复利
        BigDecimal compountAmount = Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO);
        //费用
        BigDecimal feeAmount = Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_03)).orElse(BigDecimal.ZERO);

        BigDecimal waiveAmount = Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_17)).orElse(BigDecimal.ZERO);

        debtBean.setPrincipalAmount(principalAmount);
        debtBean.setInterestAmount(interestAmount);
        debtBean.setPenaltyAmount(penaltyAmount);
        debtBean.setFeeAmount(feeAmount);
        debtBean.setCompountAmount(compountAmount);
        debtBean.setWaiveAmount(waiveAmount);
        debtBean.setTotalAmount(principalAmount.add(interestAmount).add(penaltyAmount).add(compountAmount).add(feeAmount));
        logger.info("欠款debetBean:{}", BeanUtils.toMap(debtBean));
        return debtBean;
    }

    /**
     * 根据订单查询欠款
     *
     * @param loanAccountInfo 订单
     * @param businessDate    当前业务日期
     * @param isPlanOverDue   按订单还款计划是否逾期查询(与isplanDue必须有一个为空)
     * @param isPlanDue       按订单还款计划是否到期查询
     * @param isPlanCast      按订单还款计划是否抛账
     * @param haveNotCastIntr 是否包含未抛账利息
     * @return 欠款信息
     */
    @Override
    public DebtBean getBalance(AmLoanAccountInfo loanAccountInfo, Date businessDate, Boolean isPlanOverDue,
                               Boolean isPlanDue, Boolean isPlanCast, Boolean haveNotCastIntr) {
        String loanId = loanAccountInfo.getLoanId();
        String tenantId = loanAccountInfo.getTenantId();
        String customerId = loanAccountInfo.getCustomerId();
        //该订单未还完还款计划
        List<String> planStatusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2);
        List<AmRepaymentPlanInfo> loanPlanList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanId, planStatusList, customerId, tenantId);
        DebtBean bean = this.getBalance(loanAccountInfo, loanPlanList, businessDate, isPlanOverDue, isPlanDue, isPlanCast, haveNotCastIntr);
        return bean;
    }

    /**
     * 根据订单和还款计划查询欠款
     *
     * @param loanAccountInfo 订单
     * @param loanPlanList    还款计划
     * @param businessDate    当前业务日期
     * @param isPlanOverDue   按订单还款计划是否逾期查询(与isplanDue必须有一个为空)
     * @param isPlanDue       按订单还款计划是否到期查询
     * @param isPlanCast      按订单还款计划是否抛账
     * @param haveNotCastIntr 是否包含未抛账利息
     * @return 欠款信息
     */
    @Override
    public DebtBean getBalance(AmLoanAccountInfo loanAccountInfo, List<AmRepaymentPlanInfo> loanPlanList, Date businessDate, Boolean isPlanOverDue,
                               Boolean isPlanDue, Boolean isPlanCast, Boolean haveNotCastIntr) {
        BigDecimal currPrincipalAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//本金金额
        BigDecimal currInterestAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//利息金额
        BigDecimal currPenaltyIntAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//罚息金额
        BigDecimal currCompountAmt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//罚息复利金额
        BigDecimal currServiceFeeAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//费用金额
        DebtBean debtBean = new DebtBean();
        List<DebtDetailBean> debtDetailBeanList = new ArrayList<>();
        int castLoanTenor = loanAccountInfo.getCastTenor();
        String loanId = loanAccountInfo.getLoanId();
        String tenantId = loanAccountInfo.getTenantId();
        String customerId = loanAccountInfo.getCustomerId();

        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<String> acctStatusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2);
        //该订单已抛帐交易账户
        List<AmAccountMainInfo> accountList = extAmAccountMainInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, acctStatusList);
        List<AmInterestAccrualInfo> accruAcctList = extAmInterestAccrualInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20),
                Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_STATUS_1));

        for (AmRepaymentPlanInfo planInfo : loanPlanList) {
            DebtDetailBean debtDetailBean = new DebtDetailBean();
            debtDetailBean.setTenor(planInfo.getBillingTenor());
            Date paymentDueDate = planInfo.getPaymentDueDate();

            if (isPlanOverDue != null && isPlanOverDue) {//还款计划逾期
                if (!TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                    logger.info("到期且还款计划未逾期");
                    continue;
                }
            } else if (isPlanOverDue != null && TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                logger.info("还款计划逾期");
                //还款计划没有逾期
                continue;
            }

            if (isPlanDue != null && isPlanDue) {//还款计划到期
                if (paymentDueDate.compareTo(businessDate) > 0) {
                    logger.info("还款计划到期");
                    continue;
                }
            } else if (isPlanDue != null && paymentDueDate.compareTo(businessDate) <= 0) {//还款计划没有到期
                logger.info("还款计划没有到期");
                continue;
            }

            if (isPlanCast != null && isPlanCast) {//还款计划抛账
                if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                    logger.info("还款计划抛账");
                    continue;
                }
            } else if (isPlanCast != null && !TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {//还款计划没有抛账
                logger.info("还款计划没有抛账");
                continue;
            }

            if (planInfo.getBillingTenor() <= castLoanTenor) {
                logger.info("当前期数小于等于已抛期数");
                //如果本期已经抛账
                List<AmAccountMainInfo> currAccountList = accountList.stream()
                        .filter(acct -> planInfo.getBillingTenor().equals(acct.getBillingTenor()))
                        .collect(Collectors.toList()); //获取当期账户列表
                DebtBean currBean = this.calculationDebt(currAccountList);

                //计算利息,罚息,复利累计账户中的利息金额
                BigDecimal accuTotalAccruIntr = BigDecimal.ZERO;
                BigDecimal accuTotalPentIntr = BigDecimal.ZERO;
                BigDecimal accuTotalCompountIntr = BigDecimal.ZERO;
                if (CollectionUtils.isNotEmpty(accruAcctList)) {
                    accuTotalAccruIntr = accruAcctList.stream().filter(accuAcct -> TransBizConstant.INTEREST_ACCRUAL_TYPE_66.equals(accuAcct.getAccountType()))
                            .filter(accuAcct -> planInfo.getBillingTenor().equals(accuAcct.getBillingTenor()))
                            .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
                    currInterestAmnt = currInterestAmnt.add(accuTotalAccruIntr);

                    accuTotalPentIntr = accruAcctList.stream().filter(accuAcct -> TransBizConstant.INTEREST_ACCRUAL_TYPE_10.equals(accuAcct.getAccountType()))
                            .filter(accuAcct -> planInfo.getBillingTenor().equals(accuAcct.getBillingTenor()))
                            .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
                    currPenaltyIntAmnt = currPenaltyIntAmnt.add(accuTotalPentIntr);

                    accuTotalCompountIntr = accruAcctList.stream().filter(accuAcct -> TransBizConstant.INTEREST_ACCRUAL_TYPE_20.equals(accuAcct.getAccountType()))
                            .filter(accuAcct -> planInfo.getBillingTenor().equals(accuAcct.getBillingTenor()))
                            .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
                    currCompountAmt = currCompountAmt.add(accuTotalCompountIntr);
                }

                //当期金额
                debtDetailBean.setPrincipalAmount(currBean.getPrincipalAmount());
                debtDetailBean.setInterestAmount(currBean.getInterestAmount().add(accuTotalAccruIntr));
                debtDetailBean.setPenaltyAmount(currBean.getPenaltyAmount().add(accuTotalPentIntr));
                debtDetailBean.setFeeAmount(currBean.getFeeAmount());
                debtDetailBean.setCompountAmount(currBean.getCompountAmount().add(accuTotalCompountIntr));

                currPrincipalAmnt = currPrincipalAmnt.add(currBean.getPrincipalAmount());//本期本金金额
                currInterestAmnt = currInterestAmnt.add(currBean.getInterestAmount());//本期利息金额
                currPenaltyIntAmnt = currPenaltyIntAmnt.add(currBean.getPenaltyAmount());//本期罚息金额
                currServiceFeeAmnt = currServiceFeeAmnt.add(currBean.getFeeAmount());//本期费用金额
                currCompountAmt = currCompountAmt.add(currBean.getCompountAmount());//本期罚息复利金额
            } else {
                logger.info("当前期数大于已抛期数");
                //计算剩余每期应收本金
                debtDetailBean.setPrincipalAmount(planInfo.getPrincipalAmount());
                currPrincipalAmnt = currPrincipalAmnt.add(planInfo.getPrincipalAmount()).subtract(planInfo.getRepayPrincipal());
                if (haveNotCastIntr != null && haveNotCastIntr) {
                    debtDetailBean.setInterestAmount(planInfo.getInterestAmount());
                    debtDetailBean.setFeeAmount(planInfo.getFeeAmount());
                    currInterestAmnt = currInterestAmnt.add(planInfo.getInterestAmount()).subtract(planInfo.getRepayInterest());
                    currServiceFeeAmnt = currServiceFeeAmnt.add(planInfo.getFeeAmount()).subtract(planInfo.getRepayFee());
                }
            }
            debtDetailBeanList.add(debtDetailBean);
        }

        //总金额
        BigDecimal currPostingAmnt = currPrincipalAmnt.add(currInterestAmnt).add(currServiceFeeAmnt).add(currPenaltyIntAmnt).add(currCompountAmt);
        debtBean.setTotalAmount(currPostingAmnt);
        debtBean.setPrincipalAmount(currPrincipalAmnt);
        debtBean.setFeeAmount(currServiceFeeAmnt);
        debtBean.setInterestAmount(currInterestAmnt);
        debtBean.setPenaltyAmount(currPenaltyIntAmnt);
        debtBean.setCompountAmount(currCompountAmt);
        debtBean.setDebtDetailBeans(debtDetailBeanList);
        logger.info("订单欠款金额：{}", BeanUtils.toMap(debtBean));
        return debtBean;
    }

    /**
     * 查询单期还款计划欠款
     *
     * @param planInfo
     * @return
     */
    @Override
    public DebtBean getBalance(AmRepaymentPlanInfo planInfo) {
        BigDecimal currPrincipalAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//本金金额
        BigDecimal currInterestAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//利息金额
        BigDecimal currPenaltyIntAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//罚息金额
        BigDecimal currCompountAmt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//罚息复利金额
        BigDecimal currServiceFeeAmnt = BigDecimalUtils.formatNbr(BigDecimal.ZERO);//费用金额
        String loanId = planInfo.getLoanId();
        String tenantId = planInfo.getTenantId();
        String customerId = planInfo.getCustomerId();
        int billingTenor = planInfo.getBillingTenor();


        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_03, TransBizConstant.ACCOUNT_TYPE_04,
                TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<String> acctStatusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2);
        List<AmAccountMainInfo> accountList = extAmAccountMainInfoMapper.selectAccount(loanId, customerId, tenantId, typeList, billingTenor, acctStatusList);
        DebtBean bean = this.calculationDebt(accountList);
        currPrincipalAmnt = currPrincipalAmnt.add(bean.getPrincipalAmount());
        currInterestAmnt = currInterestAmnt.add(bean.getInterestAmount());
        currPenaltyIntAmnt = currPenaltyIntAmnt.add(bean.getPenaltyAmount());
        currCompountAmt = currCompountAmt.add(bean.getCompountAmount());
        currServiceFeeAmnt = currServiceFeeAmnt.add(bean.getFeeAmount());


        List<AmInterestAccrualInfo> accruAcctList = extAmInterestAccrualInfoMapper.selectByStatusAndType(
                loanId, customerId, tenantId, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1),
                Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20), Collections.singletonList(planInfo.getBillingTenor()));

        if (CollectionUtils.isNotEmpty(accruAcctList)) {
            if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                //计算利息累计账户中的利息金额
                BigDecimal accuTotalAccruIntr = accruAcctList.stream().filter(accuAcct -> TransBizConstant.INTEREST_ACCRUAL_TYPE_66.equals(accuAcct.getAccountType()))
                        .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
                currInterestAmnt = currInterestAmnt.add(accuTotalAccruIntr);
            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                //计算罚息,复利累计账户中的罚息金额
                BigDecimal accuTotalPentIntr = accruAcctList.stream().filter(accuAcct -> TransBizConstant.INTEREST_ACCRUAL_TYPE_10.equals(accuAcct.getAccountType()))
                        .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
                currPenaltyIntAmnt = currPenaltyIntAmnt.add(accuTotalPentIntr);

                BigDecimal accuTotalCompountIntr = accruAcctList.stream().filter(accuAcct -> TransBizConstant.INTEREST_ACCRUAL_TYPE_20.equals(accuAcct.getAccountType()))
                        .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
                currCompountAmt = currCompountAmt.add(accuTotalCompountIntr);
            }
        }

        if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
            currPrincipalAmnt = currPrincipalAmnt.add(planInfo.getPrincipalAmount());
        }


            //总金额
        DebtBean debtBean = new DebtBean();
        BigDecimal currPostingAmnt = currPrincipalAmnt.add(currInterestAmnt).add(currServiceFeeAmnt).add(currPenaltyIntAmnt).add(currCompountAmt);
        debtBean.setTotalAmount(currPostingAmnt);
        debtBean.setPrincipalAmount(currPrincipalAmnt);
        debtBean.setFeeAmount(currServiceFeeAmnt);
        debtBean.setInterestAmount(currInterestAmnt);
        debtBean.setPenaltyAmount(currPenaltyIntAmnt);
        debtBean.setCompountAmount(currCompountAmt);
        logger.info("订单欠款金额：{}", BeanUtils.toMap(debtBean));
        return debtBean;
    }
}
